#include <homeworkhead.h>
#include "head.h"
#define STR_LEN 8//定义随机输出的字符串长度。
//创建新目录 
int myMkdir(task_t *pTask) 
{
    char *cond = strtok(pTask->buf, " "); //命令，cond中内容应为mkdir
    char *dirName = strtok(NULL, " ");
    MYSQL *db = pTask->db;
    //获得当前路径的目录的id作为新建逻辑目录的preid
    //select id from myNetDisk where phyaddr = 'nowPath';
    char sql[300] = "select id from myNetDisk where phyaddr = '";
    strcat(sql,pTask->nowPath);
    strcat(sql,"';");
    int qret = mysql_query(db, sql);
    //取出id
    MYSQL_RES *result = mysql_store_result(db);
    MYSQL_ROW row = mysql_fetch_row(result);
    //插入这个新建的逻辑目录，preid是之前获得的id，地址往后拼上/自己名字
    //insert into myNetDisk (fileName,user,preid,phyaddr)values('dirName','p->username',row[0],'p->nowPath');
    char newbuf[100] ={0}; //用一个新buf接收新目录逻辑路径，保持nowPath不变
    strcat(newbuf,pTask->nowPath);
    strcat(newbuf,"/");
    strcat(newbuf,dirName);
    char sql2[300] = "insert into myNetDisk (fileName,user,preid,phyaddr)values('";
    strcat(sql2,dirName);
    strcat(sql2,"','");
    strcat(sql2,pTask->usrName);
    strcat(sql2,"',");
    strcat(sql2,row[0]);
    strcat(sql2,",'");
    strcat(sql2,newbuf);
    strcat(sql2,"');");
    puts(sql2);
    qret = mysql_query(db, sql2);
    return 0;
}
//打印当前目录
int printWorkingDir(task_t *pTask)
{   
    send(pTask->netFd,pTask->nowPath,strlen(pTask->nowPath),0);
}
//删除文件
int removeFile(task_t *pTask)
{ //删除目标文件 只能删自己的逻辑文件不能删实际文件
    //不考虑删除没有的文件，会Segmentation fault至今不知道原因
    char *commond, *filename;
    int ret;
    commond = strtok(pTask->buf, " ");
    filename = strtok(NULL, " ");  //分割出文件名
    //查询条件fileName = file1 且user=用户名，
    //如果有就delete该逻辑文件返回“delete success”，没有就返回“Have not the file”
    //select id from myNetDisk where fileName = 'file1' and user='用户名'";
    MYSQL db = *pTask->db;
    char sql[300] = "select id from myNetDisk where fileName = '";
    strcat(sql,filename);
    strcat(sql,"' and user = '");
    strcat(sql,pTask->usrName);
    strcat(sql,"';");
    puts(sql);
    int qret = mysql_query(&db, sql);

    MYSQL_RES *result = mysql_store_result(&db);
    MYSQL_ROW row = mysql_fetch_row(result);//取出id
    puts(row[0]);
    //删掉这行
    //delete from myNetDisk where id = row[0];
    char sql2[300] = "delete from myNetDisk where id = ";
    strcat(sql2,row[0]);
    strcat(sql2,";");
    puts(sql2);
    qret = mysql_query(&db, sql2);
    send(pTask->netFd,"delete success",20,0);
    mysql_free_result(result);
}
//cd切换目录
int cd(task_t *pTask)
{
    char s[1024] = {0};
    strcpy(s, pTask->buf);
    char *w = strtok(s, " ");
    char orderName[512] = {0};
    char destPath[512] = {0};
    strcpy(orderName, w);//w是命令
    if ((w = strtok(NULL, " ")) != NULL)//w是路径
    {
        strcpy(destPath, w);
    }

    if (!strcmp(destPath, "-"))//返回上次目录
    {
        if (strlen(pTask->prePath) == 0)
        {
            char s[1024] = {0};
            strcpy(s, "cd :OLDPWD未设定");
            send(pTask->netFd, s, strlen(s), 0);
            return -1;
        }
        else
        {
            char temp[1024] = {0};
            strcpy(temp, pTask->nowPath);//把当前目录保存下来
            memset(pTask->nowPath,0,sizeof(pTask->nowPath));
            strcpy(pTask->nowPath,pTask->prePath);
            strcpy(pTask->prePath, temp);//把之前目录存进prePath
            send(pTask->netFd,pTask->nowPath ,strlen(pTask->nowPath), 0);
        }
    }
    else if (strlen(destPath) == 0 || !strcmp(destPath, "~"))//如果路径是空或者~回到家目录
    {
            char temp[1024] = {0};
            strcpy(temp, pTask->nowPath);//把当前目录保存下来
            memset(pTask->nowPath,0,sizeof(pTask->nowPath));
            strcat(pTask->nowPath,"/");
            strcat(pTask->nowPath,pTask->usrName);//把/用户名赋予nowPath
            strcpy(pTask->prePath, temp);//把之前目录存进prePath
            send(pTask->netFd,pTask->nowPath ,strlen(pTask->nowPath), 0);
    }
    else if(!strcmp(destPath, "."))//切换到当前目录
    {
        send(pTask->netFd,pTask->nowPath ,strlen(pTask->nowPath), 0);
    }
    else if(!strcmp(destPath, ".."))//切换到上级目录
    {
        //查询当前路径preid，如果preid是0说明已经家目录了，禁止cd
        //select preid from myNetDisk where phyaddr = 'nowPath';
        MYSQL *db = pTask->db;
        char sql[300] = "select preid from myNetDisk where phyaddr = '";
        strcat(sql,pTask->nowPath);
        strcat(sql,"';");
        int qret = mysql_query(db, sql);
        MYSQL_RES *result = mysql_store_result(db);
        MYSQL_ROW row = mysql_fetch_row(result);     //取出id
        //查询当前路径preid，如果preid不是0说明已经家目录了，则把当前路径改为上级id路径
        //puts(row[0]);
        if(strcmp(row[0],"0")==0){
            send(pTask->netFd,"已经是家目录了，无法再往上",100, 0);
            return 0;
        }
        //select phyaddr from myNetDisk where id = row[0];
        char sql2[300] = "select phyaddr from myNetDisk where id = ";
        strcat(sql2,row[0]);
        strcat(sql2,";");
        qret = mysql_query(db, sql2);
        MYSQL_RES *result2 = mysql_store_result(db);
        MYSQL_ROW row2 = mysql_fetch_row(result2);     //取出路径
        //查询当前路径preid，如果preid不是0说明已经家目录了，则把当前路径改为上级id路径
        puts(row2[0]);
        char temp[1024] = {0};
        strcpy(temp, pTask->nowPath);//把当前目录保存下来
        memset(pTask->nowPath,0,sizeof(pTask->nowPath));
        strcpy(pTask->nowPath,row2[0]);
        strcpy(pTask->prePath, temp);//把之前目录存进prePath
        send(pTask->netFd,pTask->nowPath ,strlen(pTask->nowPath), 0);
    }    
    else if(strlen(destPath) != 0)//切换到指定路径
    {
        puts(destPath);
        //查询该路径是否属于该用户，如果不属于则不可跳转
        //select user from myNetDisk where phyaddr = 'destPath';
        MYSQL *db = pTask->db;
        char sql[300] = "select user from myNetDisk where phyaddr = '";
        strcat(sql,pTask->nowPath);
        strcat(sql,"/");
        strcat(sql,destPath);
        strcat(sql,"';");
        puts(sql);
        int qret = mysql_query(db, sql);
        MYSQL_RES *result = mysql_store_result(db);
        MYSQL_ROW row = mysql_fetch_row(result);     //取出user
        puts(row[0]);
        //如果不是自己用户目录则不能切换路径
        if(strcmp(row[0],pTask->usrName)!=0){
            send(pTask->netFd,"那不是你的目录",50, 0);
            return 0;
        }else{
            char temp[1024] = {0};
            strcpy(temp, pTask->nowPath);//把当前目录保存下来
            strcat(pTask->nowPath,"/");
            strcat(pTask->nowPath,destPath);
            strcpy(pTask->prePath, temp);//把之前目录存进prePath
            send(pTask->netFd,pTask->nowPath ,strlen(pTask->nowPath), 0);
        }
    }
    return 0;
}
//ls查看当前目录
int ls(task_t *pTask)
{
    MYSQL *db = pTask->db;
    //获得当前路径的目录的id作为展示文件的preid且user=用户名
    //select id from myNetDisk where phyaddr = 'nowPath';
    char sql[300] = "select id from myNetDisk where phyaddr = '";
    strcat(sql,pTask->nowPath);
    strcat(sql,"';");
    puts(sql);
    int qret = mysql_query(db, sql);
    //取出id
    MYSQL_RES *result = mysql_store_result(db);
    puts("111");
    MYSQL_ROW row = mysql_fetch_row(result);
    puts(row[0]);
    //select fileName from myNetDisk where user = '用户名' and preid = row[0];
    char sql2[300] = "select fileName from myNetDisk where user = '";
    strcat(sql2,pTask->usrName);
    strcat(sql2,"' and preid=");
    strcat(sql2,row[0]);
    strcat(sql2,";");
    puts(sql2);
    qret = mysql_query(db, sql2);
    //取出fileName
    MYSQL_RES *result2 = mysql_store_result(db);
    MYSQL_ROW row2;
    //mysql_fetch_row流结构要放在循环里，mysql_num_fields指每一行打印多少列，
    //如果只有1行但是你要打印3列，那肯定就Segmentation fault
    char lsbuf[1024] = {0};
    while((row2=mysql_fetch_row(result2))!=NULL){
        for(int i = 0;i<mysql_num_fields(result2);++i){
            strcat(lsbuf,row2[i]);
            strcat(lsbuf," ");
        }
        //fflush(stdout);
    }
    //puts(lsbuf);
    send(pTask->netFd,lsbuf,strlen(lsbuf),0);
    mysql_free_result(result2);
}
//传文件给客户端
int downloadFile(task_t *pTask)
{
    // gets file1
    char *command = strtok(pTask->buf, " "); //解析获取命令
    char *fileName = strtok(NULL, " ");      //解析获取文件名
    int length = strlen(fileName);

    //首先判断文件是否存在,文件存在是传输文件的必要条件！
    DIR *pdir = opendir(".");
    ERROR_CHECK(pdir, NULL, "open");
    struct dirent *pdirent;
    while ((pdirent = readdir(pdir)) != NULL){
        if (!strcmp(pdirent->d_name, fileName)){
            break;
        }
    }
    if ((pdirent = readdir(pdir)) == NULL){
        puts("No such file or directory!");
        send(pTask->netFd, "No such file or directory!", 26, 0);
        return -1;
    }

    char pianyi[100] = {0};
    recv(pTask->netFd, pianyi, 100, 0); //接收客户端发来的客户端本地文件的大小，也就是偏移量
    //服务端再把字符串类改回long类型
    long pianyilen = atoi(pianyi); //偏移量也有可能是0
    // printf("pianyilen = %d\n",pianyilen);

    train_t t;
    bzero(t.buf, sizeof(t.buf));
    t.dataLength = length;
    strcpy(t.buf, fileName);

    printf("length = %d,fileName = %s\n", length, fileName);

    int fd = open(fileName, O_RDWR);
    ERROR_CHECK(fd, -1, "open");
    // int ret = send(pTask->netFd,&t,sizeof(t.dataLength)+ t.dataLength,MSG_NOSIGNAL);//发送文件名
    // ERROR_CHECK(ret,-1,"send");
    //不再需要发送文件名，客户端自己获取
    //获取服务器文件总大小并发送
    struct stat statbuf;
    int ret = fstat(fd, &statbuf); //获取文件的总大小
    ERROR_CHECK(ret, -1, "fstat");
    t.dataLength = sizeof(statbuf.st_size); //把文件的大小装入小火车
    memcpy(t.buf, &statbuf.st_size, sizeof(statbuf.st_size));
    send(pTask->netFd, &t, sizeof(t.dataLength) + t.dataLength, MSG_NOSIGNAL);

    //对比进行判断
    long totalFileLen = statbuf.st_size;
    printf("pianyilen = %ld,totallen = %ld\n", pianyilen, totalFileLen);
    if (pianyilen < totalFileLen)
    {
        lseek(fd, pianyilen, SEEK_SET);
    }
    else
    {
        // printf("Recv success!\n");
        return 0;
    }

    char *p = (char *)mmap(NULL, statbuf.st_size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
    ERROR_CHECK(p, MAP_FAILED, "mmap");
    off_t total = pianyilen;
    while (total < statbuf.st_size)
    {
        if (statbuf.st_size - total < sizeof(t.buf))
        {
            //最后一次发送
            t.dataLength = statbuf.st_size - total;
        }
        else
        {
            t.dataLength = sizeof(t.buf);
        }
        memcpy(t.buf, p + total, t.dataLength); //把数据装入火车车厢
        int ret = send(pTask->netFd, &t, sizeof(t.dataLength) + t.dataLength, MSG_NOSIGNAL);
        ERROR_CHECK(ret, -1, "send");
        total += t.dataLength;
    }
    munmap(p, statbuf.st_size);
    t.dataLength = 0;
    send(pTask->netFd, &t, sizeof(t.dataLength), 0);
    //发送终止
    close(fd);
}
int recvn(int netFd, void *p, int n)
{
    int total = 0;
    int ret;
    char *pTmp = (char *)p;
    while (total < n)
    {
        ret = recv(netFd, pTmp + total, n - total, 0);
        ERROR_CHECK(ret, -1, "recv");
        total += ret;
    }
    return 0;
}
//接收从客户端上传来的文件
int uploadFile(task_t *pTask)
{
    int dataLength;
    char name[1024] = {0};
    char md[100]={0};
    //收文件名
    int ret = recvn(pTask->netFd, &dataLength, sizeof(int)); //先收火车头
    ERROR_CHECK(ret, -1, "recv");
    ret = recvn(pTask->netFd, name, dataLength); //再收文件名
    ERROR_CHECK(ret, -1, "recv");

    //收md值
    ret = recv(pTask->netFd,md, sizeof(md),0);
    ERROR_CHECK(ret, -1, "recv md");
    //printf("md = %s\n",md);
    //将获得的md值与数据库内的mdkey比较，如果没有就上传实际文件与逻辑文件
    //如果存在该文件就只上传逻辑文件，实现秒传功能
    MYSQL *db = pTask->db;

    //首先获得当前目录的id作为文件的preid
    //select id from myNetDisk where phyaddr='pTask->nowPath';
    char sql2[300] = "select id from myNetDisk where phyaddr='";
    strcat(sql2,pTask->nowPath);
    strcat(sql2,"';");
    puts(sql2);
    int qret = mysql_query(db, sql2);//获得当前目录id
    MYSQL_RES *result2 = mysql_store_result(db);
    MYSQL_ROW row2 = mysql_fetch_row(result2);
    puts("------当前目录id----------");
    puts(row2[0]);

    //select * from myNetDisk where mdkey='md值';
    char sql[300] = "select user from myNetDisk where mdkey = '";
    strcat(sql,md);
    strcat(sql,"';");
    puts(sql);
    qret = mysql_query(db, sql);//获得实际文件的拥有者用户名
    MYSQL_RES *result = mysql_store_result(db);
    MYSQL_ROW row = mysql_fetch_row(result);
    if(row == NULL){
        puts("之前没有该文件");
        goto next;
    }
    puts("--------实际文件用户名--------");
    puts(row[0]);
    //如果存在该文件但是自己用户没有，需要数据库加一个逻辑文件
    if(row != NULL && strcmp(row[0],pTask->usrName)!=0){//秒传成功
        //insert into myNetDisk (fileName,user,preid,mdkey,phyaddr)values('file1','liguohao',2,'16ee403b065897d3c6cb3335fa18eca5','p->nowPath');
        char sql3[300] = "insert into myNetDisk (fileName,user,preid,mdkey,phyaddr)values('";
        strcat(sql3,name);
        strcat(sql3,"','");
        strcat(sql3,pTask->usrName);
        strcat(sql3,"',");
        strcat(sql3,row2[0]);
        strcat(sql3,",'");
        strcat(sql3,md);
        strcat(sql3,"','");
        strcat(sql3,pTask->nowPath);
        strcat(sql3,"');");
        puts(sql3);
        qret = mysql_query(db, sql3);
        send(pTask->netFd,"1",2,0);
        return 1;
    }
    else if(row != NULL && strcmp(row[0],pTask->usrName)==0){//存在该文件
        send(pTask->netFd,"2",2,0);
        return 2;
    }


next:   
    send(pTask->netFd,"3",2,0);//三种情况发送三种信号
    //否则就是不存在该文件，接着上传
    int fd = open(name, O_RDWR | O_CREAT, 0666);
    ERROR_CHECK(fd, -1, "open");
    char buf[1024] = {0};
    long fileSize;
    recvn(pTask->netFd, &dataLength, sizeof(int));    //先收火车头
    recvn(pTask->netFd, &fileSize, sizeof(fileSize)); //再收文件长度
    printf("fileSize = %ld\n", fileSize);
    off_t downSize = 0;
    off_t lastSize = 0;
    off_t slice = fileSize / 1000;
    struct timeval begTime, endTime;
    gettimeofday(&begTime, NULL);
    while (1)
    {
        ret = recvn(pTask->netFd, &dataLength, sizeof(int)); //先收火车头
        ERROR_CHECK(ret, -1, "recv");
        if (dataLength == 0)
        {
            break;
        }
        if (dataLength != 1000)
        {
            printf("dataLength = %d\n", dataLength);
        }
        ret = recvn(pTask->netFd, buf, dataLength);
        ERROR_CHECK(ret, -1, "recv");
        downSize += dataLength;
        if (downSize - lastSize > slice)
        {
            printf("%5.2lf%s\r", 100.0 * downSize / fileSize, "%");
            fflush(stdout);
            lastSize = downSize;
        }
        write(fd, buf, dataLength);
    }
    printf("100.00%s\n", "%");
    gettimeofday(&endTime, NULL);
    printf("total time = %ld\n", 1000000 * (endTime.tv_sec - begTime.tv_sec) + endTime.tv_usec - begTime.tv_usec);

    //上传完毕后 数据库也插入一个逻辑文件
    //insert into myNetDisk (fileName,user,preid,mdkey,phyaddr)values('文件名字','liguohao',row2[0],'16ee403b065897d3c6cb3335fa18eca5','p->nowPath');
    char sql4[300] = "insert into myNetDisk (fileName,user,preid,mdkey,phyaddr)values('";
    strcat(sql4,name);
    strcat(sql4,"','");
    strcat(sql4,pTask->usrName);
    strcat(sql4,"',");
    strcat(sql4,row2[0]);
    strcat(sql4,",'");
    strcat(sql4,md);
    strcat(sql4,"','");
    strcat(sql4,pTask->nowPath);
    strcat(sql4,"');");
    puts(sql4);
    qret = mysql_query(db, sql4);
    ret = send(pTask->netFd,"成功上传实际与逻辑文件",40,0);
    ERROR_CHECK(ret,-1,"send end");
}
//生成随机盐值
void GenerateStr(char *salt)
{
    char str[STR_LEN + 1] = {0};
    int i, flag;
    srand(time(NULL)); //通过时间函数设置随机数种子，使得每次运行结果随机。
    for (i = 0; i < STR_LEN; i++)
    {
        flag = rand() % 3;
        switch (flag)
        {
        case 0:
            str[i] = rand() % 26 + 'a';
            break;
        case 1:
            str[i] = rand() % 26 + 'A';
            break;
        case 2:
            str[i] = rand() % 10 + '0';
            break;
        }
    }
    strcpy(salt,str);
    //printf("salt = %s\n",salt);
}
//拼接盐值
void saltSplice(char *salt){
    char buf[100]={0};
    strcpy(buf,"$6$");
    strcat(buf,salt);
    strcat(buf,"$");
    strcpy(salt,buf);
    //puts(salt);
}
//连接数据库
void mysqlInit(task_t *pTask)
{
    MYSQL *db = mysql_init(NULL);
    pTask->db = db;
    char *host = "localhost";   //写自己本地ip也行
    char *user = "liguohao"; //目前只给root和liguohao两个用户加了mysql权限
    char *password = "1234";
    char *database = "test42";
    MYSQL *ret = mysql_real_connect(db, host, user, password, database, 0, NULL, 0);
    if (ret != NULL){
        // printf("db = %p,ret = %p\n",db,ret);
        strcat(pTask->nowPath,"/");//task获得当前用户逻辑目录路径
        strcat(pTask->nowPath,pTask->usrName);
        printf("db connect success\n");
    }else{
        printf("Error:%s\n", mysql_error(db));
        exit(-1);
    }
}
//注册账号
int mysqlRegister(task_t *pTask)
{
    task_t sqlTask = *pTask;
    MYSQL *db = pTask->db;
    char sPwd[100]={0};
    saltSplice(sqlTask.salt);
    strcpy(sPwd,crypt(sqlTask.usePwd,sqlTask.salt));
    //insert into userInfo values(chao,salt,密文);
    char sql[300] = "insert into userInfo values('";
    strcat(sql,sqlTask.usrName);
    strcat(sql,"','");
    strcat(sql,sqlTask.salt);
    strcat(sql,"','");
    strcat(sql,sPwd);
    strcat(sql,"');");
    puts(sql);
    int qret = mysql_query(db, sql);
    if (qret != 0)
    {
        printf("Error: %s\n", mysql_error(db));
        exit(-1);
    }
    //注册的同时在数据库建一个逻辑目录，与用户名同名，且逻辑地址为/用户名
    //insert into myNetDisk (fileName,user,preid,phyaddr)values('liguohao','liguohao',0,'/liguohao');
    char sql2[300] = "insert into myNetDisk (fileName,user,preid,phyaddr)values('";
    strcat(sql2,sqlTask.usrName);
    strcat(sql2,"','");
    strcat(sql2,sqlTask.usrName);
    strcat(sql2,"',0,'/");
    strcat(sql2,sqlTask.usrName);
    strcat(sql2,"');");
    puts(sql2);
    qret = mysql_query(db, sql2);
    if (qret != 0)
    {
        printf("Error: %s\n", mysql_error(db));
        exit(-1);
    }
    send(sqlTask.netFd,"注册成功！",20,0);
}
//登录账号验证
int mysqlLogin(task_t *pTask){
    task_t sqlTask = *pTask;
    MYSQL *db = pTask->db;
    //select * from userInfo where userName = 'chao';
    //拼接字符串
    char sql[300] = "select salt from userInfo where userName = '";
    strcat(sql,sqlTask.usrName);
    strcat(sql,"';");
    int qret = mysql_query(db, sql);
    if (qret != 0){//如果用户名不对会返回-1
        printf("Error: %s\n", mysql_error(db));
        exit(-1);
    }
    //取出salt
    MYSQL_RES *result = mysql_store_result(db);
    MYSQL_ROW row = mysql_fetch_row(result);
    char sPwd[100] ={0};//通过salt获得密文
    if(row  != NULL){
        //printf("%s\n",row[0]);
        strcpy(sPwd,crypt(sqlTask.usePwd,row[0]));
    }else{
        return -1;
    }
    //puts(sPwd);
    //取得数据库里面的密文
    char sql2[300] = "select secretPwd from userInfo where userName = '";
    strcat(sql2,sqlTask.usrName);
    strcat(sql2,"';");
    //puts(sql2);
    qret = mysql_query(db, sql2);
    MYSQL_RES *result2 = mysql_store_result(db);
    MYSQL_ROW row2 = mysql_fetch_row(result2);
    //puts(row2[0]);
    if(strcmp(sPwd,row2[0])==0)//和从数据库取出的密文比较
    {
        return 0;
    }else{
        return -1;
    }
}